package com.tsfyun.scm.gateway.filter;

import com.tsfyun.scm.gateway.config.properties.NoticeProperties;
import com.tsfyun.scm.gateway.support.dd.DingTalkNoticeUtil;
import com.tsfyun.scm.gateway.util.IpUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 *  处理响应的 的filter
 * @author huangting
 */
public class ResponseFilter implements GlobalFilter, Ordered {

    private static final String START_TIME = "startTime";

    //业务请求执行时间提醒阀值
    private static final BigDecimal REMIND_EXECUTE_TIME = new BigDecimal("1.5");

    @Autowired
    private NoticeProperties noticeProperties;

    @Value("${gateway.manage.salt:asdfg12345}")
    private String salt;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //执行完成后 进行调用耗时埋点
        exchange.getAttributes().put(START_TIME, System.currentTimeMillis());

        //原始响应类
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        //重新包装的响应类
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                    //如果响应过大，会进行截断，出现乱码，然后看api DefaultDataBufferFactory有个join方法可以合并所有的流，乱码的问题解决
                    DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                    DataBuffer join = dataBufferFactory.join(dataBuffer);
                    byte[] content = new byte[join.readableByteCount()];
                    join.read(content);
                    //释放掉内存
                    DataBufferUtils.release(join);
                    //打印响应日志
                    logResponse(exchange, new String(content, StandardCharsets.UTF_8));

                    return bufferFactory.wrap(content);
                }));
            }
        };

        return chain.filter(exchange.mutate().response(decoratedResponse).build())
                .then(Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(START_TIME);
                    if (startTime != null) {
                        Long executeTime = (System.currentTimeMillis() - startTime);
                        if(new BigDecimal(executeTime).divide(new BigDecimal("1000"),2,BigDecimal.ROUND_HALF_UP).compareTo(REMIND_EXECUTE_TIME) > -1) {
                            //发送钉钉通知
                            List<String> dingdingUrls = noticeProperties.getDingdingUrl();
                            DingTalkNoticeUtil.send2DingDingException(exchange.getRequest(),"请求响应慢",null,dingdingUrls,salt);
                        }
                    }
                }));
    }


    /**
     * 打印响应报文
     *
     * @param exchange
     */
    public void logResponse(ServerWebExchange exchange, String response) {
        ServerHttpRequest request = exchange.getRequest();
        String ip = IpUtils.getIpAddress(request);
    }


    @Override
    public int getOrder() {
        return -3;
    }
}